package com.tender.shiro.filter;

import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.redisson.api.RDeque;
import org.redisson.api.RedissonClient;
import com.tender.util.ShiroUtil;
import com.tender.domain.ShiroUser;
import com.tender.constants.Constants;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

@Slf4j
public class KickedOutAuthorizationFilter extends AccessControlFilter {

    private RedissonClient redissonClient;

    private SessionDAO redisSessionDao;

    private DefaultWebSessionManager sessionManager;

    public KickedOutAuthorizationFilter(RedissonClient redissonClient, SessionDAO redisSessionDao, DefaultWebSessionManager sessionManager) {
        this.redissonClient = redissonClient;
        this.redisSessionDao = redisSessionDao;
        this.sessionManager = sessionManager;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        // 直接交给下面的方法：onAccessDenied
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        Subject subject = getSubject(servletRequest, servletResponse);
        if (!subject.isAuthenticated()) {
            // 如果没有登录，直接进行之后的流程：直接放行执行后面的filter和servlet，
            return true;
        }
        // 存放session对象进入队列
        String sessionId = ShiroUtil.getShiroSessionId();
        // 两个classLoader 不一样，所以不能强转
        ShiroUser shiroUser = ShiroUtil.getShiroUser();
        Assert.notNull(shiroUser, "登录用户不能为空");
        RDeque<String> queue = redissonClient.getDeque("KickedOutAuthorizationFilter: " + shiroUser.getLoginName());
        // 判断sessionId是否存在于此用户的队列中
        boolean flag = queue.contains(sessionId);
        if (!flag) {
            queue.addLast(sessionId);
        }
        //如果此时队列大于1，则开始踢人
        if (queue.size() > Constants.CURRENT_LOGIN_NUM_4_LOGIN_NAME) {
            sessionId = queue.getFirst();
            queue.removeFirst(); // 从头开始删除，即之前登录的从分布式队列中删除。
            Session session = null;
            try {
                session = sessionManager.getSession(new DefaultSessionKey(sessionId));
            } catch (UnknownSessionException ex) {
                log.info("session已经失效");
            } catch (ExpiredSessionException expiredSessionException) {
                log.info("session已经过期");
            }
            if (null != session) {
                redisSessionDao.delete(session);
            }
        }

        // 进行之后的流程：直接放行执行后面的filter和servlet，
        return true;
    }
}
